package org.jsola.hr.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.jsola.admin.entity.HeaderFieldsDO;
import org.jsola.admin.vo.HeaderFieldsListVO;
import org.jsola.admin.vo.HeaderFieldsVO;
import org.jsola.common.DateKit8;
import org.jsola.common.FileKit;
import org.jsola.common.PageKit;
import org.jsola.core.Page;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.*;
import org.jsola.hr.constant.SalaryConstants;
import org.jsola.hr.dto.HrCustomerDetailsNewAddDTO;
import org.jsola.hr.entity.CompanyCustomGroupingDO;
import org.jsola.hr.entity.CompanyDO;
import org.jsola.hr.entity.EmpInfoDO;
import org.jsola.hr.entity.GroupCompanyDO;
import org.jsola.hr.provider.IAdminProviderService;
import org.jsola.hr.provider.IInsuranceProviderService;
import org.jsola.hr.provider.ISalaryProviderService;
import org.jsola.hr.provider.IUserProviderService;
import org.jsola.hr.query.HrEmployeeSalaryNewQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.*;
import org.jsola.insurance.query.QyInsuranceRecordQuery;
import org.jsola.salary.constant.HeaderType;
import org.jsola.salary.dto.*;
import org.jsola.salary.entity.*;
import org.jsola.salary.vo.*;
import org.jsola.user.core.TokenUser;
import org.jsola.user.entity.UserDO;
import org.jsola.user.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.jsola.salary.constant.CustomerDetailsType.CUSTOMER;


/**
 * 子公司分管公司关系表
 *
 * @author Mr.Zheng
 */
@Service("hrHrCustomerDetailsServiceNewImpl")
@Slf4j
public class HrCustomerDetailsServiceNewImpl implements IHrCustomerDetailsNewService {


    @Autowired
    private ISalaryProviderService salaryProviderService;
    @Autowired
    private IEmpSocInfoService empSocInfoService;
    @Autowired
    private IChildCompanyService childCompanyService;
    @Autowired
    private IGroupCompanyService groupCompanyService;
    @Autowired
    private IInsuranceProviderService insuranceProviderService;
    @Autowired
    private ICompanyCustomGroupingService companyCustomGroupingService;
    @Autowired
    private IUserProviderService userProviderService;
    @Autowired
    private ICompanyService companyService;
    @Autowired
    private IAdminProviderService adminProviderService;
    @Autowired
    private IEmpInfoService empInfoService;


    // 一次性服务费(薪资组，仅在saveByCompanyIds方法及子方法中使用)
    private BigDecimal oneServiceChargeAmount = BigDecimal.ZERO;

    private static final String DATE_FORMAT = "yyyyMMddHHmmss";


    private final static List<String> TOTAL_TABLE_HEADER_LIST1 = new ArrayList<>(Arrays.asList(
            "应发工资", "实发工资", "企业社保", "企业公积金", "人力成本", "薪资服务费", "社保服务费"));

    private final static List<String> TOTAL_TABLE_HEADER_LIST2 = new ArrayList<>(Arrays.asList(
            "服务费", "社保滞纳金", "客户费用"));


    @Override
    public List<CustomerDetailsNewDO> saveByCompanyIds(HrCustomerDetailsNewAddDTO hrCustomerDetailsNewAddDTO, TokenUser tokenUser) {
        // 校验
        ParamException.isFalse(
                (hrCustomerDetailsNewAddDTO.getSalaryMonthly() == null || hrCustomerDetailsNewAddDTO.getSalaryYearly() == null)
                        && (hrCustomerDetailsNewAddDTO.getSocialMonthly() == null || hrCustomerDetailsNewAddDTO.getSocialYearly() == null)
                        && CollectionUtils.isEmpty(hrCustomerDetailsNewAddDTO.getServiceChargeIds())
                , "薪资年月、社保年月、服务费项不能同时为空");
        Integer yearly = hrCustomerDetailsNewAddDTO.getYearly();
        Integer monthly = hrCustomerDetailsNewAddDTO.getMonthly();
        // 类型校验 表头
        Integer salaryYearly = hrCustomerDetailsNewAddDTO.getSalaryYearly();
        Integer salaryMonthly = hrCustomerDetailsNewAddDTO.getSalaryMonthly();
        Integer socialYearly = hrCustomerDetailsNewAddDTO.getSocialYearly();
        Integer socialMonthly = hrCustomerDetailsNewAddDTO.getSocialMonthly();
        List<HrCustomerDetailsNewAddDTO.CompanyInfo> companyInfoList = hrCustomerDetailsNewAddDTO.getCompanyInfoList();
        // 用工单位去重
        companyInfoList = companyInfoList.parallelStream().filter(
                distinctByKey(HrCustomerDetailsNewAddDTO.CompanyInfo::getCompanyId)).collect(Collectors.toList());
        // 用工单位Id-名称
        Map<String, String> companyIdNameMap = companyInfoList.parallelStream()
                .collect(Collectors.toMap(HrCustomerDetailsNewAddDTO.CompanyInfo::getCompanyId
                        , HrCustomerDetailsNewAddDTO.CompanyInfo::getCompanyName, (v1, v2) -> v1));
        List<String> companyIds = new ArrayList<>(companyIdNameMap.keySet());
        // 0、全局变量一次性服务费使用完，初始化
        oneServiceChargeAmount = BigDecimal.ZERO;

        // 1、单纯保存客户明细表
        List<CustomerDetailsNewDO> customerDetailsNewDOList = saveCustomerDetails(hrCustomerDetailsNewAddDTO
                , yearly, monthly, salaryYearly, salaryMonthly, socialYearly, socialMonthly
                , companyIds, companyInfoList, tokenUser);

        // 2、保存员工明细
        // empId-项目名称-金额(第二返回值使用)
        Map<String, Map<String, BigDecimal>> empIdAmountMap = new HashMap<>(16);
        List<EmployeeDetailsNewDO> employeeDetailsNewDOList = saveEmployeeDetails(yearly, monthly, companyIds
                , salaryYearly, salaryMonthly, socialYearly, socialMonthly, customerDetailsNewDOList
                , empIdAmountMap, tokenUser);

        // 3、保存明细金额(员工明细+客户明细)
        // 服务费项Ids
        List<Long> serviceChargeIds = hrCustomerDetailsNewAddDTO.getServiceChargeIds();
        Map<Long, List<DetailsAmountDO>> customerDetailsNewIdListMap =
                saveDetailsAmount(customerDetailsNewDOList, employeeDetailsNewDOList, empIdAmountMap, serviceChargeIds, tokenUser);

        // 4、 生成客户明细表 保存同步生成客户结算
        saveCustomerSettlement(hrCustomerDetailsNewAddDTO.getType(), customerDetailsNewDOList, customerDetailsNewIdListMap, tokenUser);

        // 客户明细金额
        return customerDetailsNewDOList;
    }


    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    @Override
    public int deleteByIds(TokenUser tokenUser, Long... customerDetailsIds) {
        ParamException.notEmpty(customerDetailsIds, "未选择需要删除的数据");
        int detailsByIds = salaryProviderService.deleteNewCustomerDetailsByIds(tokenUser, customerDetailsIds);
        List<EmployeeDetailsNewDO> employeeDetailsNewDOList = salaryProviderService
                .selectNewEmployeeDetailsByCustomerDetailsIdList(Arrays.asList(customerDetailsIds), tokenUser);
        // 同时删除员工明细
        salaryProviderService.deleteNewCustomerDetailsByIds(tokenUser, employeeDetailsNewDOList.parallelStream()
                .map(BaseDO::getId).toArray(Long[]::new));
        return detailsByIds;
    }

    @Override
    public CustomerDetailsNewDO selectLastOne(TokenUser tokenUser) {
        // 登录用户拥有的用工单位权限
        UserCompanyVO userCompanyVO = childCompanyService.selectByUserId(tokenUser.getUserId(), tokenUser.getSiteId());
        if (userCompanyVO.getIsAllChildCompany()) {
            return salaryProviderService.selectNewCustomerDetailsLastOne(null, tokenUser);
        }
        List<String> companyIdList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(userCompanyVO.getCompanyIds())) {
            companyIdList.addAll(userCompanyVO.getCompanyIds());
        }
        if (!CollectionUtils.isEmpty(userCompanyVO.getGroupIds())) {
            List<GroupCompanyDO> groupCompanyDOList = groupCompanyService
                    .selectByGroupIds(userCompanyVO.getGroupIds(), tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(groupCompanyDOList)) {
                companyIdList.addAll(groupCompanyDOList.parallelStream()
                        .map(GroupCompanyDO::getCompanyId).distinct().collect(Collectors.toList()));
            }
        }
        return salaryProviderService.selectNewCustomerDetailsLastOne(companyIdList, tokenUser);
    }

    @Override
    public JfStatisticsAnalyseVO jfStatisticsAnalyseLaborCosts(String companyId, Integer yearly, TokenUser tokenUser) {
        // 人力成本
        JfStatisticsAnalyseListVO jfStatisticsAnalyseLaborCosts
                = salaryProviderService.jfStatisticsAnalyseLaborCosts(companyId, yearly, tokenUser);
        List<Map<String, JfStatisticsAnalyseItemVO>> itemMapList = jfStatisticsAnalyseLaborCosts.getItemMapList();
        // 人力成本
        Map<String, JfStatisticsAnalyseItemVO> laborCostItemVoMap = new HashMap<>(16);
        // 应发工资
        Map<String, JfStatisticsAnalyseItemVO> shouldWagesItemVoMap = new HashMap<>(16);
        // 企业社保公积金
        Map<String, JfStatisticsAnalyseItemVO> enterpriseItemVoMap = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(itemMapList)) {
            laborCostItemVoMap = itemMapList.parallelStream().map(map -> map.get("人力成本"))
                    .filter(Objects::nonNull).collect(Collectors.toMap(JfStatisticsAnalyseItemVO::getMonth, v -> v));
            shouldWagesItemVoMap = itemMapList.parallelStream().map(map -> map.get("应发工资"))
                    .filter(Objects::nonNull).collect(Collectors.toMap(JfStatisticsAnalyseItemVO::getMonth, v -> v));
            enterpriseItemVoMap = itemMapList.parallelStream().map(map -> map.get("企业社保公积金"))
                    .filter(Objects::nonNull).collect(Collectors.toMap(JfStatisticsAnalyseItemVO::getMonth, v -> v));
        }
        // 商保
        QyInsuranceRecordQuery qyInsuranceRecordQuery = new QyInsuranceRecordQuery();
        qyInsuranceRecordQuery.setCompanyId(companyId);
        qyInsuranceRecordQuery.setYear(yearly + "");
        org.jsola.insurance.vo.JfStatisticsAnalyseVO jfStatisticsAnalyseInsurance =
                insuranceProviderService.jfStatisticsAnalyseInsurance(qyInsuranceRecordQuery, tokenUser);
        List<org.jsola.insurance.vo.JfStatisticsAnalyseItemVO> insuranceItemList = jfStatisticsAnalyseInsurance.getItemList();
        Map<String, org.jsola.insurance.vo.JfStatisticsAnalyseItemVO> monthJfStatisticsAnalyseItemMap = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(insuranceItemList)) {
            monthJfStatisticsAnalyseItemMap = insuranceItemList.parallelStream()
                    .collect(Collectors.toMap(org.jsola.insurance.vo.JfStatisticsAnalyseItemVO::getMonth, v -> v));
        }
        List<JfStatisticsAnalyseItemVO> rtList = new ArrayList<>();
        // 应发工资
        BigDecimal shouldWagesStatistics = BigDecimal.ZERO;
        // 社保
        BigDecimal enterpriseStatistics = BigDecimal.ZERO;
        // 商保
        BigDecimal insuranceStatistics = BigDecimal.ZERO;
        for (int i = 1; i < 13; i++) {
            JfStatisticsAnalyseItemVO rtVO = new JfStatisticsAnalyseItemVO();
            // 人力成本金额
            JfStatisticsAnalyseItemVO laborCostItemVO = laborCostItemVoMap.get(i + "");
            BigDecimal laborCostsYearFormer = BigDecimal.ZERO;
            BigDecimal laborCostsYearCurrent = BigDecimal.ZERO;
            if (laborCostItemVO != null) {
                laborCostsYearFormer = BigDecimalKit.nullToZero(laborCostItemVO.getYearFormer());
                laborCostsYearCurrent = BigDecimalKit.nullToZero(laborCostItemVO.getYearCurrent());
            }
            JfStatisticsAnalyseItemVO shouldWagesItemVO = shouldWagesItemVoMap.get(i + "");
            if (shouldWagesItemVO != null) {
                shouldWagesStatistics = shouldWagesStatistics.add(BigDecimalKit.nullToZero(shouldWagesItemVO.getYearCurrent()));
            }
            JfStatisticsAnalyseItemVO enterpriseItemVO = enterpriseItemVoMap.get(i + "");
            if (enterpriseItemVO != null) {
                enterpriseStatistics = enterpriseStatistics.add(BigDecimalKit.nullToZero(enterpriseItemVO.getYearCurrent()));
            }

            // 商保金额
            String month;
            if (i < 10) {
                month = "0" + i;
            } else {
                month = i + "";
            }
            org.jsola.insurance.vo.JfStatisticsAnalyseItemVO insuranceItemVO = monthJfStatisticsAnalyseItemMap.get(month);
            BigDecimal insuranceYearFormer = BigDecimal.ZERO;
            BigDecimal insuranceYearCurrent = BigDecimal.ZERO;
            if (insuranceItemVO != null) {
                insuranceYearFormer = BigDecimalKit.nullToZero(insuranceItemVO.getYearFormer());
                insuranceYearCurrent = BigDecimalKit.nullToZero(insuranceItemVO.getYearCurrent());
            }
            laborCostsYearFormer = laborCostsYearFormer.add(insuranceYearFormer);
            laborCostsYearCurrent = laborCostsYearCurrent.add(insuranceYearCurrent);
            insuranceStatistics = insuranceStatistics.add(insuranceYearCurrent);
            // 非0判断
            BigDecimal yearOverYearGrowthRate = null;
            if (BigDecimal.ZERO.compareTo(laborCostsYearFormer) != 0 && BigDecimal.ZERO.compareTo(laborCostsYearCurrent) != 0) {
                yearOverYearGrowthRate = laborCostsYearCurrent.subtract(laborCostsYearFormer)
                        .divide(laborCostsYearFormer, 4, RoundingMode.HALF_UP).stripTrailingZeros();
            }
            rtVO.setMonth(i + "");
            rtVO.setYearFormer(laborCostsYearFormer);
            rtVO.setYearCurrent(laborCostsYearCurrent);
            rtVO.setYearOverYearGrowthRate(yearOverYearGrowthRate);
            rtList.add(rtVO);
        }
        JfStatisticsAnalyseVO jfStatisticsAnalyseVO = new JfStatisticsAnalyseVO();
        jfStatisticsAnalyseVO.setStatisticsName(jfStatisticsAnalyseLaborCosts.getStatisticsName());
        jfStatisticsAnalyseVO.setYear(jfStatisticsAnalyseLaborCosts.getYear());
        jfStatisticsAnalyseVO.setItemList(rtList);
        List<JfStatisticsAnalyseVO.Percentage> percentageList = new ArrayList<>();
        JfStatisticsAnalyseVO.Percentage laborCostsPercentage = new JfStatisticsAnalyseVO.Percentage();
        BigDecimal all = shouldWagesStatistics.add(enterpriseStatistics).add(insuranceStatistics);
        if (BigDecimal.ZERO.compareTo(all) != 0) {
            laborCostsPercentage.setName("薪资成本");
            BigDecimal shouldWagesProportion = shouldWagesStatistics.divide(all, 4, RoundingMode.HALF_UP).stripTrailingZeros();
            laborCostsPercentage.setProportion(shouldWagesProportion);
            percentageList.add(laborCostsPercentage);

            JfStatisticsAnalyseVO.Percentage shouldWagesPercentage = new JfStatisticsAnalyseVO.Percentage();
            shouldWagesPercentage.setName("社保成本");
            BigDecimal enterpriseProportion = enterpriseStatistics.divide(all, 4, RoundingMode.HALF_UP).stripTrailingZeros();
            if ((shouldWagesProportion.add(enterpriseProportion)).compareTo(BigDecimal.ONE) > 0) {
                enterpriseProportion = BigDecimal.ONE.subtract(shouldWagesProportion);
            }
            shouldWagesPercentage.setProportion(enterpriseProportion);
            percentageList.add(shouldWagesPercentage);

            JfStatisticsAnalyseVO.Percentage enterprisePercentage = new JfStatisticsAnalyseVO.Percentage();
            enterprisePercentage.setName("商保成本");
            enterprisePercentage.setProportion(BigDecimal.ONE.subtract(shouldWagesProportion).subtract(enterpriseProportion));
            percentageList.add(enterprisePercentage);
            jfStatisticsAnalyseVO.setPercentageList(percentageList);
        }
        return jfStatisticsAnalyseVO;
    }

    @Override
    public Page<HrMonthlySalarySummaryNewVO> selectSalarySummaryBySiteAndMonthly(Integer yearly, Integer monthly
            , String customerDetailsType, String companyId, String companyName, Integer pageNo, Integer pageSize, TokenUser tokenUser) {
        // 1、当前登录人分管用工单位Ids(如果没有指定用工单位Ids)
        List<String> companyIds;
        if (StringUtils.isEmpty(companyId)) {
            companyIds = companyService.tileCompanyIdByUser(tokenUser).parallelStream().distinct().collect(Collectors.toList());
        } else {
            companyIds = Collections.singletonList(companyId);
        }
        if (!StringUtils.isEmpty(companyName)) {
            List<CompanyDO> companyDOList = companyService.selectByIds(companyIds, tokenUser.getSiteId());
            companyIds = companyDOList.parallelStream().filter(companyDO -> companyDO.getName().contains(companyName))
                    .map(BaseDO::getId).collect(Collectors.toList());
        }
        // 2、查询分管用工单位在指定月份创建的客户明细
        Page<CustomerDetailsNewVO> page = salaryProviderService
                .selectPageAndAmountByTypeAndCompanyIds(yearly, monthly, customerDetailsType, companyIds, pageNo, pageSize, tokenUser);
        List<CustomerDetailsNewVO> customerDetailsNewVOList = page.getEntities();
        if (org.springframework.util.CollectionUtils.isEmpty(customerDetailsNewVOList)) {
            return new Page<>(pageSize, pageNo);
        }
        // 3、查询创建人名称
        List<UserDO> userDOList = userProviderService.selectUserByIds(customerDetailsNewVOList.parallelStream()
                .map(CustomerDetailsNewVO::getCreateUserId).collect(Collectors.toList()));
        Map<String, String> userIdNameMap = userDOList.parallelStream().collect(Collectors.toMap(BaseDO::getId, UserDO::getName));
        // 4、将客户明细转成汇总的类型返回
        // 服务费项
        List<CustomerDetailsNewServiceChargeDO> customerDetailsNewServiceChargeDOList = salaryProviderService
                .selectCustomerDetailsNewServiceChargeByCustomerDetailsNewIds(
                        customerDetailsNewVOList.parallelStream().map(CustomerDetailsNewVO::getId).collect(Collectors.toList()), tokenUser);
        // 客户明细表-服务费项
        Map<Long, List<CustomerDetailsNewServiceChargeDO>> customerDetailsNewIdListMap = customerDetailsNewServiceChargeDOList
                .parallelStream().collect(Collectors.groupingBy(CustomerDetailsNewServiceChargeDO::getCustomerDetailsNewId));
        // 合计表头
        List<String> totalTableHeaderList = new ArrayList<>(TOTAL_TABLE_HEADER_LIST1);
        if (!CollectionUtils.isEmpty(customerDetailsNewServiceChargeDOList)) {
            totalTableHeaderList.addAll(customerDetailsNewServiceChargeDOList.parallelStream()
                    .map(CustomerDetailsNewServiceChargeDO::getName).distinct().collect(Collectors.toList()));
        }
        totalTableHeaderList.addAll(TOTAL_TABLE_HEADER_LIST2);
        List<HrMonthlySalarySummaryNewVO> hrMonthlySalarySummaryNewVOList = new ArrayList<>();
        for (CustomerDetailsNewVO customerDetailsNewVO : customerDetailsNewVOList) {
            String customerDetailsNewCompanyId = customerDetailsNewVO.getCompanyId();
            HrMonthlySalarySummaryNewVO hrMonthlySalarySummaryNewVO = new HrMonthlySalarySummaryNewVO();
            hrMonthlySalarySummaryNewVO.setYearly(customerDetailsNewVO.getYearly());
            hrMonthlySalarySummaryNewVO.setMonthly(customerDetailsNewVO.getMonthly());
            hrMonthlySalarySummaryNewVO.setSalaryYearly(customerDetailsNewVO.getSalaryYearly());
            hrMonthlySalarySummaryNewVO.setSalaryMonthly(customerDetailsNewVO.getSalaryMonthly());
            hrMonthlySalarySummaryNewVO.setSocialYearly(customerDetailsNewVO.getSocialYearly());
            hrMonthlySalarySummaryNewVO.setSocialMonthly(customerDetailsNewVO.getSocialMonthly());
            hrMonthlySalarySummaryNewVO.setPersonalPeriod(yearly + "年" + monthly + "月");
            hrMonthlySalarySummaryNewVO.setCompanyIds(Collections.singletonList(customerDetailsNewCompanyId));
            hrMonthlySalarySummaryNewVO.setCustomerDetailsType(customerDetailsNewVO.getType());
            hrMonthlySalarySummaryNewVO.setSubjectType("company");
            hrMonthlySalarySummaryNewVO.setSubjectId(customerDetailsNewCompanyId);
            hrMonthlySalarySummaryNewVO.setName(customerDetailsNewVO.getName());
            hrMonthlySalarySummaryNewVO.setEmpNumber(customerDetailsNewVO.getEmpNumber());
            hrMonthlySalarySummaryNewVO.setCustomerDetailsIds(Collections.singletonList(customerDetailsNewVO.getId()));
            List<DetailsAmountVO> detailsAmountVOList = customerDetailsNewVO.getDetailsAmountVOList();
            if (CollectionUtils.isEmpty(detailsAmountVOList)) {
                detailsAmountVOList = new ArrayList<>();
            }
            List<CustomerDetailsNewServiceChargeDO> customerDetailsNewServiceChargeList
                    = customerDetailsNewIdListMap.get(customerDetailsNewVO.getId());
            if (!CollectionUtils.isEmpty(customerDetailsNewServiceChargeList)) {
                for (CustomerDetailsNewServiceChargeDO customerDetailsNewServiceChargeDO : customerDetailsNewServiceChargeList) {
                    DetailsAmountVO detailsAmountVO = new DetailsAmountVO();
                    detailsAmountVO.setSourceType("customer");
                    detailsAmountVO.setSourceId(customerDetailsNewVO.getId());
                    detailsAmountVO.setName(customerDetailsNewServiceChargeDO.getName());
                    detailsAmountVO.setAmount(customerDetailsNewServiceChargeDO.getAmount());
                    detailsAmountVOList.add(detailsAmountVO);
                }
            }
            // 排序
            if (!CollectionUtils.isEmpty(detailsAmountVOList)) {
                detailsAmountVOList = detailsAmountVOList.parallelStream().map(detailsAmountVO -> {
                    int sort = totalTableHeaderList.indexOf(detailsAmountVO.getName());
                    // 不存在的跳过
                    if (sort == -1) {
                        return null;
                    }
                    detailsAmountVO.setSort(sort);
                    return detailsAmountVO;
                }).filter(Objects::nonNull).sorted(Comparator.comparing(DetailsAmountVO::getSort)).collect(Collectors.toList());
            }
            hrMonthlySalarySummaryNewVO.setDetailsAmountVOList(detailsAmountVOList);
            hrMonthlySalarySummaryNewVO.setGmtCreate(customerDetailsNewVO.getGmtCreate());
            hrMonthlySalarySummaryNewVO.setCreateUserId(customerDetailsNewVO.getCreateUserId());
            hrMonthlySalarySummaryNewVO.setCreateUserName(userIdNameMap.get(customerDetailsNewVO.getCreateUserId()));
            hrMonthlySalarySummaryNewVOList.add(hrMonthlySalarySummaryNewVO);
        }
        return new Page<>(hrMonthlySalarySummaryNewVOList, pageNo, pageSize, page.getEntityCount());
    }

    @Override
    public Page<HrMonthlySalarySummaryNewVO> selectCustomGroupingBySiteAndMonthly(Integer yearly, Integer monthly
            , String customerDetailsType, Integer pageNo, Integer pageSize, TokenUser tokenUser) {
        // 客户明细的moduleCode为customerDetails
        List<CompanyCustomGroupingDO> companyCustomGroupingDOList = companyCustomGroupingService
                .selectAndSaveBySiteAndMonthly(yearly, monthly, "customerDetails", tokenUser);
        // 转分页
        Page<CompanyCustomGroupingDO> page = PageKit.startPage(companyCustomGroupingDOList, pageNo, pageSize);
        List<CompanyCustomGroupingDO> entities = page.getEntities();
        List<HrMonthlySalarySummaryNewVO> hrMonthlySalarySummaryNewVOList = new ArrayList<>();
        for (CompanyCustomGroupingDO companyCustomGroupingDO : entities) {
            HrMonthlySalarySummaryNewVO hrMonthlySalarySummaryNewVO = selectSalarySummaryByCustomGroupingId(
                    yearly, monthly, customerDetailsType, companyCustomGroupingDO.getId(), tokenUser);
            hrMonthlySalarySummaryNewVOList.add(hrMonthlySalarySummaryNewVO);
        }
        return new Page<>(hrMonthlySalarySummaryNewVOList, pageNo, pageSize, page.getEntityCount());
    }

    @Override
    public HeaderFieldsVO selectHeaderDetailNew(String subjectType, String subjectId, List<Long> customerDetailsIds
            , Integer yearly, Integer monthly, TokenUser tokenUser) {
        ParamException.notEmpty(customerDetailsIds, "客户明细表未生成，请生成后再查看");
        HeaderFieldsVO headerFieldsVO = new HeaderFieldsVO();
        List<HeaderFieldsListVO> headerFieldsListVOList;
        String sourceId;
        // 用工单位分组：group 自定义用工单位分组：customGrouping
        if ("group".equals(subjectType) || "customGrouping".equals(subjectType)) {
            sourceId = "" + yearly + monthly;
            headerFieldsListVOList = selectAllCustomerHeaderDetailNew(subjectType, subjectId, sourceId, customerDetailsIds, tokenUser);
        } else if ("company".equals(subjectType)) {
            // 类型为用工单位
            Long customerDetailsId = customerDetailsIds.get(0);
            sourceId = customerDetailsId + "";
            // 自定表头
            List<HeaderFieldsDO> headerFieldsDOList = adminProviderService
                    .selectMyselfHeaderFieldsBySourceId(subjectType, subjectId, HeaderType.CUSTOMER.getValue(), sourceId, tokenUser);
            if (org.springframework.util.CollectionUtils.isEmpty(headerFieldsDOList)) {
                // 没创建自定义表头 取客户明细表表头
                headerFieldsListVOList = selectAllCustomerHeaderDetailNew(subjectType, subjectId, customerDetailsId, tokenUser);
            } else {
                // 已创建自定义表头
                headerFieldsListVOList = headerFieldsDOList.parallelStream().map(headerFieldsDO
                                -> headerFieldsDO.to(HeaderFieldsListVO.class))
                        .sorted(Comparator.comparing(HeaderFieldsListVO::getSort)).collect(Collectors.toList());
            }
        } else {
            throw new ParamException("查询类型有误，类型：" + subjectType);
        }
        // 可见表头字段
        List<HeaderFieldsListVO> canSee = new ArrayList<>();
        // 不可见表头字段
        List<HeaderFieldsListVO> ignore = new ArrayList<>();
        for (HeaderFieldsListVO headerFieldsListVO : headerFieldsListVOList) {
            if (headerFieldsListVO.getIsOpen()) {
                canSee.add(headerFieldsListVO);
            } else {
                ignore.add(headerFieldsListVO);
            }
        }
        headerFieldsVO.setId("all");
        headerFieldsVO.setSubjectType(subjectType);
        headerFieldsVO.setSubjectId(subjectId);
        headerFieldsVO.setModuleCode(HeaderType.CUSTOMER.getValue());
        headerFieldsVO.setSourceId(sourceId);
        headerFieldsVO.setCanSee(canSee);
        headerFieldsVO.setIgnore(ignore);
        return headerFieldsVO;
    }

    @Override
    public Page<EmployeeDetailsNewVO> selectEmployeeDetailsPageByCompanyIdsNew(HrEmployeeSalaryNewQuery hrEmployeeSalaryNewQuery
            , TokenUser tokenUser) {
        List<EmployeeDetailsNewVO> employeeDetailsNewVOList = selectEmployeeDetailsByCompanyIdsNew(hrEmployeeSalaryNewQuery, tokenUser);
        if (org.springframework.util.CollectionUtils.isEmpty(employeeDetailsNewVOList)) {
            return new Page<>(hrEmployeeSalaryNewQuery.getPageSize(), hrEmployeeSalaryNewQuery.getPageNo());
        }
        return PageKit.startPage(employeeDetailsNewVOList, hrEmployeeSalaryNewQuery.getPageNo(), hrEmployeeSalaryNewQuery.getPageSize());
    }

    @Override
    public String exportExcelNew(HrEmployeeSalaryNewQuery hrEmployeeSalaryNewQuery, TokenUser tokenUser) {
        String type = hrEmployeeSalaryNewQuery.getType();
        String id = hrEmployeeSalaryNewQuery.getId();
        List<Long> customerDetailsIdList = hrEmployeeSalaryNewQuery.getCustomerDetailsIdList();
        Integer yearly = hrEmployeeSalaryNewQuery.getYearly();
        Integer monthly = hrEmployeeSalaryNewQuery.getMonthly();
        // 要导出的数据
        List<EmployeeDetailsNewVO> employeeDetailsNewVOList = selectEmployeeDetailsByCompanyIdsNew(hrEmployeeSalaryNewQuery, tokenUser);
        ParamException.notEmpty(employeeDetailsNewVOList, "未选择需要导出的数据");

        // 本月人数
        int empNumber = 0;
        List<CustomerDetailsNewDO> customerDetailsNewDOList = salaryProviderService
                .selectCustomerDetailsNewByIds(customerDetailsIdList, tokenUser);
        if (CollectionUtils.isEmpty(customerDetailsNewDOList)) {
            log.error("客户明细表已不存在，请重新选择，customerDetailsIdList：{}", customerDetailsIdList);
            throw new ParamException("客户明细表已不存在，请重新选择");
        }
        for (CustomerDetailsNewDO customerDetailsNewDO : customerDetailsNewDOList) {
            // 计薪人数
            if (customerDetailsNewDO.getEmpNumber() != null) {
                empNumber += customerDetailsNewDO.getEmpNumber();
            }
        }
        // 明细金额
        List<DetailsAmountDO> detailsAmountDOList = salaryProviderService.selectDetailsAmountByTypeAndIds(
                "customer", customerDetailsIdList, tokenUser);
        // 服务费项
        List<CustomerDetailsNewServiceChargeDO> customerDetailsNewServiceChargeDOList = salaryProviderService
                .selectCustomerDetailsNewServiceChargeByCustomerDetailsNewIds(
                        customerDetailsIdList, tokenUser);
        for (CustomerDetailsNewServiceChargeDO customerDetailsNewServiceChargeDO : customerDetailsNewServiceChargeDOList) {
            DetailsAmountDO detailsAmountDO = new DetailsAmountDO();
            detailsAmountDO.setSourceType("customer");
            detailsAmountDO.setSourceId(customerDetailsNewServiceChargeDO.getCustomerDetailsNewId());
            detailsAmountDO.setName(customerDetailsNewServiceChargeDO.getName());
            detailsAmountDO.setAmount(customerDetailsNewServiceChargeDO.getAmount());
            detailsAmountDOList.add(detailsAmountDO);
        }
        // 客户明细/统计报表
        String headName;
        // 制表时间
        String gmtCreateStr = "";
        // 制表人
        String userName = "";
        if ("company".equals(type)) {
            CustomerDetailsNewDO customerDetailsNewDO = customerDetailsNewDOList.get(0);
            headName = customerDetailsNewDO.getName();
            // 制表时间
            Date gmtCreate = customerDetailsNewDO.getGmtCreate();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
            gmtCreateStr = sdf.format(gmtCreate);
            // 制表人
            String createUserId = customerDetailsNewDO.getCreateUserId();
            UserVO userVO = userProviderService.selectById(createUserId);
            if (userVO == null) {
                userName = "制表人已删除";
            } else {
                userName = userVO.getName();
            }
        } else if ("customGrouping".equals(type)) {
            CompanyCustomGroupingDO companyCustomGroupingDO = companyCustomGroupingService.selectDOById(Long.valueOf(id)
                    , tokenUser.getSiteId());
            if (companyCustomGroupingDO == null) {
                log.error("自定义分组不存在，Id:{}", id);
                throw new ParamException("自定义分组不存在，请刷新后重试");
            }
            headName = companyCustomGroupingDO.getName();
        } else {
            log.error("传入客户明细表类型有误，类型：{}", type);
            throw new ParamException("传入客户明细表类型有误");
        }
        // 表头
        List<List<String>> headList = new ArrayList<>();
        List<String> oneHead;
        // 合计表头
        List<String> totalTableHeaderList = new ArrayList<>(TOTAL_TABLE_HEADER_LIST1);
        if (!CollectionUtils.isEmpty(customerDetailsNewServiceChargeDOList)) {
            totalTableHeaderList.addAll(customerDetailsNewServiceChargeDOList.parallelStream()
                    .map(CustomerDetailsNewServiceChargeDO::getName).distinct().collect(Collectors.toList()));
        }
        totalTableHeaderList.addAll(TOTAL_TABLE_HEADER_LIST2);
        // 自定义表头
        HeaderFieldsVO headerFieldsVO = selectHeaderDetailNew(type, id, customerDetailsIdList, yearly
                , monthly, tokenUser);
        ParamException.notNull(headerFieldsVO, "查询表头错误，请刷新后重试");
        List<HeaderFieldsListVO> canSee = headerFieldsVO.getCanSee();
        ParamException.notEmpty(canSee, "无需要导出项，请重新设置表头后导出");

        List<String> fieldNames = canSee.stream().map(HeaderFieldsListVO::getFieldName).collect(Collectors.toList());
        for (int n = 0; n < fieldNames.size(); n++) {
            String fieldName = fieldNames.get(n);
            // 表头
            oneHead = new ArrayList<>();
            Map<String, BigDecimal> nameAmount = new HashMap<>(16);
            if (!org.springframework.util.CollectionUtils.isEmpty(detailsAmountDOList)) {
                for (DetailsAmountDO detailsAmountDO : detailsAmountDOList) {
                    BigDecimal amount;
                    try {
                        amount = new BigDecimal(detailsAmountDO.getAmount());
                    } catch (Exception e) {
                        continue;
                    }
                    nameAmount.merge(detailsAmountDO.getName(), amount, BigDecimal::add);
                }
            }
            List<DetailsAmountVO> detailsAmountVOList = new ArrayList<>();
            for (int i = 0; i < totalTableHeaderList.size(); i++) {
                String name = totalTableHeaderList.get(i);
                DetailsAmountVO detailsAmountVO = new DetailsAmountVO();
                detailsAmountVO.setName(name);
                if (nameAmount.get(name) == null) {
                    continue;
                }
                detailsAmountVO.setAmount(nameAmount.get(name).stripTrailingZeros().toPlainString());
                detailsAmountVO.setSort(i + 1);
                detailsAmountVOList.add(detailsAmountVO);
            }
            oneHead.add("【" + headName + "】" + yearly + "年" + monthly + "月 客户明细表");
            String firstLineHead = "本月人数：" + empNumber + "人     " +
                    detailsAmountVOList.parallelStream().sorted(Comparator.comparing(DetailsAmountVO::getSort))
                            .map(detailsAmountVO -> detailsAmountVO.getName() + "：" + detailsAmountVO.getAmount())
                            .collect(Collectors.joining("     "));
            oneHead.add(firstLineHead);
            oneHead.add(fieldName);
            headList.add(oneHead);
        }
        // 定义所有的数据集合
        List<List<String>> dataList = new ArrayList<>();
        // 员工金额详情
        List<DetailsAmountDO> empAmountDOList = salaryProviderService.selectDetailsAmountByTypeAndIds("employee",
                employeeDetailsNewVOList.parallelStream().map(EmployeeDetailsNewVO::getId).collect(Collectors.toList()), tokenUser);
        Map<Long, List<DetailsAmountDO>> employeeSalaryNewIdListMap = empAmountDOList
                .parallelStream().collect(Collectors.groupingBy(DetailsAmountDO::getSourceId));
        // 定义每一条数据
        for (EmployeeDetailsNewVO employeeDetailsNewVO : employeeDetailsNewVOList) {
            List<String> oneData = new ArrayList<>();
            // 自定义项
            Map<String, BigDecimal> nameBigDecimalMap = new HashMap<>();
            List<DetailsAmountDO> detailsAmountList = employeeSalaryNewIdListMap.get(employeeDetailsNewVO.getId());
            if (!org.springframework.util.CollectionUtils.isEmpty(detailsAmountList)) {
                nameBigDecimalMap = detailsAmountList.parallelStream()
                        .collect(Collectors.toMap(DetailsAmountDO::getName
                                , detailsAmountDO -> new BigDecimal(detailsAmountDO.getAmount())));
            }
            for (String fieldName : fieldNames) {
                if ("姓名".equals(fieldName)) {
                    oneData.add(employeeDetailsNewVO.getEmpName());
                } else if ("身份证号".equals(fieldName)) {
                    oneData.add(employeeDetailsNewVO.getIdCard());
                } else {
                    oneData.add(BigDecimalKit.nullToString(nameBigDecimalMap.get(fieldName)));
                }
            }
            dataList.add(oneData);
        }
        // 复合人 + 制表人
        if ("company".equals(type) && fieldNames.size() > 5) {
            List<String> oneData = new ArrayList<>();
            for (int i = 0; i < fieldNames.size(); i++) {
                if (i == 0) {
                    oneData.add("复核人：");
                } else if (i == fieldNames.size() - 2) {
                    oneData.add("制表人：");
                } else if (i == fieldNames.size() - 1) {
                    oneData.add(userName);
                } else {
                    oneData.add("");
                }
            }
            dataList.add(oneData);
            List<String> lastData = new ArrayList<>();
            for (int i = 0; i < fieldNames.size(); i++) {
                if (i == 0) {
                    lastData.add("日期：");
                } else if (i == fieldNames.size() - 2) {
                    lastData.add("日期：");
                } else if (i == fieldNames.size() - 1) {
                    lastData.add(gmtCreateStr);
                } else {
                    lastData.add("");
                }
            }
            dataList.add(lastData);
        }
        List<CellRowHeightStyleStrategy.Style> styleList = new ArrayList<>();
        // 第一行表名行高
        CellRowHeightStyleStrategy.Style style1 = new CellRowHeightStyleStrategy.Style();
        style1.setRowIndex(0);
        style1.setHeight((short) 1000);
        styleList.add(style1);
        // 第二行统计行高
        CellRowHeightStyleStrategy.Style style2 = new CellRowHeightStyleStrategy.Style();
        style2.setRowIndex(1);
        style2.setHeight((short) 1000);
        styleList.add(style2);
        // 第三行表头行高
        CellRowHeightStyleStrategy.Style style3 = new CellRowHeightStyleStrategy.Style();
        style3.setRowIndex(2);
        style3.setHeight((short) 600);
        styleList.add(style3);
        // 导出excel
        return generateExcel(type, "客户明细", headList, "客户明细", dataList, styleList);
    }

    /**
     * 导出
     *
     * @param type      客户明细表类型
     * @param name      导出名称
     * @param headList  表头List
     * @param sheetName sheet名称
     * @param dataList  数据List
     * @param styleList 自定义行高
     * @return
     */
    private static String generateExcel(String type, String name, List<List<String>> headList, String sheetName
            , List<List<String>> dataList, List<CellRowHeightStyleStrategy.Style> styleList) {
        // 创建需要导出的文件
        String tempFilePath = FileKit.getSystemTempPath();
        String fileName = name + DateKit8.format(new Date(), DATE_FORMAT) + ".xlsx";
        boolean mkdir = new File(tempFilePath).mkdir();
        tempFilePath += fileName;
        File file = new File(tempFilePath);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = generalTactics(true);
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(tempFilePath);
        List<CellStyleModel> cellStyleList = new ArrayList<>();
        // 第一行表名样式
        cellStyleList.add(CellStyleModel.createBackgroundColorCellStyleModel(
                "客户明细", 0, 0, 112, 173, 71));
        // 第二行统计样式
        cellStyleList.add(CellStyleModel.createBackgroundColorCellStyleModel(
                "客户明细", 1, 0, 169, 208, 142));
        // 第三行表头样式
        for (int i = 0; i < headList.size(); i++) {
            cellStyleList.add(CellStyleModel.createBackgroundColorCellStyleModel(
                    "客户明细", 2, i, 226, 239, 218));
        }

        if ("company".equals(type) && !CollectionUtils.isEmpty(dataList)) {
            // 复核人样式
            cellStyleList.add(CellStyleModel.createBackgroundColorCellStyleModel("客户明细"
                    , dataList.size() + 1, 0, 169, 208, 142));
            // 复核人对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 1, 0, HorizontalAlignment.LEFT, VerticalAlignment.BOTTOM));
            // 复核人名称对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 1, 1, HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM));
            // 制表人样式
            cellStyleList.add(CellStyleModel.createBackgroundColorCellStyleModel("客户明细"
                    , dataList.size() + 1, dataList.get(0).size() - 2, 169, 208, 142));
            // 制表人对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 1, dataList.get(0).size() - 2, HorizontalAlignment.LEFT, VerticalAlignment.BOTTOM));
            // 制表人对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 1, dataList.get(0).size() - 1, HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM));
            // 复核人下方日期样式
            cellStyleList.add(CellStyleModel.createBackgroundColorCellStyleModel("客户明细"
                    , dataList.size() + 2, 0, 169, 208, 142));
            // 复核人下方日期对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 2, 0, HorizontalAlignment.LEFT, VerticalAlignment.BOTTOM));
            // 复核人下方日期内容对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 2, 1, HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM));
            // 制表人下方日期样式
            cellStyleList.add(CellStyleModel.createBackgroundColorCellStyleModel("客户明细"
                    , dataList.size() + 2, dataList.get(0).size() - 2, 169, 208, 142));
            // 复核人下方日期对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 2, dataList.get(0).size() - 2, HorizontalAlignment.LEFT, VerticalAlignment.BOTTOM));
            // 复核人下方日期对齐方式
            cellStyleList.add(CellStyleModel.createAlignmentCellStyleModel("客户明细"
                    , dataList.size() + 2, dataList.get(0).size() - 1, HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM));
        }
        excelWriterBuilder.registerWriteHandler(new CustomCellStyleHandler(3, cellStyleList)).build();
        ExcelWriterSheetBuilder builder = excelWriterBuilder.head(headList).sheet(sheetName)
                .registerWriteHandler(new AdaptiveColumnWidthStrategy());
        if (!org.springframework.util.CollectionUtils.isEmpty(styleList)) {
            builder.registerWriteHandler(new CellRowHeightStyleStrategy(styleList));
        }
        builder.registerWriteHandler(horizontalCellStyleStrategy);
        builder.doWrite(dataList);
        return tempFilePath;
    }

    /**
     * 通用内容和头策略
     */
    private static HorizontalCellStyleStrategy generalTactics(Boolean center) {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //字体
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 11);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        if (center) {
            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        }
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }


    /**
     * 查询员工明细
     *
     * @param hrEmployeeSalaryNewQuery 查询条件
     * @param tokenUser                登录用户
     * @return 员工明细
     */
    private List<EmployeeDetailsNewVO> selectEmployeeDetailsByCompanyIdsNew(HrEmployeeSalaryNewQuery hrEmployeeSalaryNewQuery
            , TokenUser tokenUser) {
        List<Long> customerDetailsIdList = hrEmployeeSalaryNewQuery.getCustomerDetailsIdList();
        if (org.springframework.util.CollectionUtils.isEmpty(customerDetailsIdList)) {
            return new ArrayList<>();
        }
        List<EmployeeDetailsNewDO> employeeDetailsNewDOList = salaryProviderService
                .selectNewEmployeeDetailsByCustomerDetailsIdList(customerDetailsIdList, tokenUser);
        if (org.springframework.util.CollectionUtils.isEmpty(employeeDetailsNewDOList)) {
            return new ArrayList<>();
        }
        // 明细金额
        List<DetailsAmountDO> detailsAmountDOList = salaryProviderService.selectDetailsAmountByTypeAndIds("employee"
                , employeeDetailsNewDOList.parallelStream().map(BaseDO::getId).collect(Collectors.toList()), tokenUser);
        // 员工明细Id-明细金额List
        Map<Long, List<DetailsAmountDO>> employeeDetailsNewListMap = detailsAmountDOList.parallelStream()
                .collect(Collectors.groupingBy(DetailsAmountDO::getSourceId));
        // 员工Id
        List<String> empIds = employeeDetailsNewDOList.parallelStream().map(EmployeeDetailsNewDO::getEmpId).distinct().collect(Collectors.toList());
        // 员工信息
        List<EmpInfoDO> empInfoDOList = empInfoService.selectDoByIdsIgnoreValid(empIds);
        Map<String, EmpInfoDO> empInfoDOMap = empInfoDOList.parallelStream().collect(Collectors.toMap(BaseDO::getId, v -> v));
        return employeeDetailsNewDOList.parallelStream().map(employeeDetailsDO -> {
            EmployeeDetailsNewVO employeeDetailsNewVO = employeeDetailsDO.to(EmployeeDetailsNewVO.class);
            List<DetailsAmountDO> detailsAmountList = employeeDetailsNewListMap.get(employeeDetailsNewVO.getId());
            // 放入明细金额
            if (org.springframework.util.CollectionUtils.isEmpty(detailsAmountList)) {
                employeeDetailsNewVO.setDetailsAmountVOList(new ArrayList<>());
            } else {
                employeeDetailsNewVO.setDetailsAmountVOList(detailsAmountList.parallelStream()
                        .map(detailsAmountDO -> detailsAmountDO.to(DetailsAmountVO.class)).parallel().collect(Collectors.toList()));
            }
            // 放入员工信息
            EmpInfoDO empInfoDO = empInfoDOMap.get(employeeDetailsDO.getEmpId());
            if (empInfoDO != null) {
                employeeDetailsNewVO.setIdCard(empInfoDO.getIdCard());
                employeeDetailsNewVO.setPhone(empInfoDO.getPhone());
                employeeDetailsNewVO.setEmpStatus(empInfoDO.getStatus());
            } else {
                employeeDetailsNewVO.setIdCard("");
                employeeDetailsNewVO.setPhone("");
                employeeDetailsNewVO.setEmpStatus(0);
            }
            // 姓名、手机号、身份证号
            if (org.apache.commons.lang3.StringUtils.isNotBlank(hrEmployeeSalaryNewQuery.getNameOrPhoneOrIdCard())) {
                if (!employeeDetailsNewVO.getEmpName().contains(hrEmployeeSalaryNewQuery.getNameOrPhoneOrIdCard())
                        && !employeeDetailsNewVO.getIdCard().contains(hrEmployeeSalaryNewQuery.getNameOrPhoneOrIdCard())
                        && !employeeDetailsNewVO.getPhone().contains(hrEmployeeSalaryNewQuery.getNameOrPhoneOrIdCard())) {
                    return null;
                }
            }
            // 员工状态筛选
            if (hrEmployeeSalaryNewQuery.getEmpStatus() != null
                    && !hrEmployeeSalaryNewQuery.getEmpStatus().equals(employeeDetailsNewVO.getEmpStatus())) {
                return null;
            }
            return employeeDetailsNewVO;
        }).filter(Objects::nonNull).parallel().collect(Collectors.toList());
    }


    /**
     * 获取用工单位级客户明细表表头
     *
     * @param subjectType       业务类型
     * @param subjectId         业务Id
     * @param customerDetailsId 客户明细Id
     * @param tokenUser         当前用户
     * @return 用工单位级客户明细表表头
     */
    private List<HeaderFieldsListVO> selectAllCustomerHeaderDetailNew(String subjectType, String subjectId
            , Long customerDetailsId, TokenUser tokenUser) {
        List<HeaderFieldsListVO> headerFieldsListVOList = new ArrayList<>();
        CustomerDetailsNewDO customerDetailsNewDO = salaryProviderService.selectCustomerDetailsNewById(customerDetailsId, tokenUser);
        ParamException.notNull(customerDetailsNewDO, "该客户明细已不存在，请刷新后重试");
        List<String> headerList = new ArrayList<>(Arrays.asList(customerDetailsNewDO.getHeader().split(",")));
        HeaderFieldsListVO headerFieldsListVO = new HeaderFieldsListVO();
        headerFieldsListVO.setSubjectType(subjectType);
        headerFieldsListVO.setSubjectId(subjectId);
        headerFieldsListVO.setSourceId(customerDetailsId + "");
        headerFieldsListVO.setModuleCode(HeaderType.CUSTOMER.getValue());
        headerFieldsListVO.setIsOpen(true);
        headerFieldsListVO.setIsEdit(true);
        headerFieldsListVO.setModuleType("客户明细");
        for (int i = 0; i < headerList.size(); i++) {
            String header = headerList.get(i);
            HeaderFieldsListVO empName = headerFieldsListVO.to(HeaderFieldsListVO.class);
            empName.setFieldName(header);
            empName.setFieldEnName(SalaryConstants.CUSTOMER_DETAILS_MAP.get(header));
            empName.setSort(i);
            headerFieldsListVOList.add(empName);
        }
        return headerFieldsListVOList;
    }

    /**
     * 获取用工单位分组级客户明细表表头
     *
     * @param subjectType        业务类型
     * @param subjectId          业务Id
     * @param sourceId           来源Id(年 + 月)
     * @param customerDetailsIds 客户明细Ids
     * @param tokenUser          当前用户
     * @return 用工单位分组级客户明细表表头
     */
    private List<HeaderFieldsListVO> selectAllCustomerHeaderDetailNew(String subjectType, String subjectId
            , String sourceId, List<Long> customerDetailsIds, TokenUser tokenUser) {
        List<HeaderFieldsListVO> headerFieldsListVOList = new ArrayList<>();
        // 自定义表头
        List<HeaderFieldsDO> headerFieldsDOList = adminProviderService
                .selectMyselfHeaderFieldsBySourceId(subjectType, subjectId, HeaderType.CUSTOMER.getValue(), sourceId, tokenUser);
        // 客户明细表表头
        List<CustomerDetailsNewDO> customerDetailsNewDOList = salaryProviderService.selectCustomerDetailsNewByIds(customerDetailsIds, tokenUser);
        String headerString = customerDetailsNewDOList.parallelStream().map(CustomerDetailsNewDO::getHeader)
                .filter(org.apache.commons.lang3.StringUtils::isNotBlank).collect(Collectors.joining(","));
        List<String> headerList = Arrays.stream(headerString.split(",")).distinct().collect(Collectors.toList());
        boolean hadEmpName = headerList.remove("姓名");
        boolean hadIdCard = headerList.remove("身份证号");
        boolean hadShouldWages = headerList.remove("应发工资");
        boolean hadSocCity = headerList.remove("参保城市");
        boolean hadEmpSocialSecurity = headerList.remove("个人社保");
        boolean hadEnterpriseSocialSecurity = headerList.remove("企业社保");
        boolean hadEmpProvidentFund = headerList.remove("个人公积金");
        boolean hadEnterpriseProvidentFund = headerList.remove("企业公积金");
        boolean hadTax = headerList.remove("个人所得税");
        boolean hadPaidWages = headerList.remove("实发工资");
        boolean hadLaborCosts = headerList.remove("人力成本");
        boolean hadFine = headerList.remove("社保滞纳金");
        boolean hadSalaryServiceCharge = headerList.remove("薪资服务费");
        boolean hadSalarySocialCharge = headerList.remove("社保服务费");
        boolean hadServiceCharge = headerList.remove("服务费");
        // 构建表头数据
        HeaderFieldsListVO headerFieldsListVO = new HeaderFieldsListVO();
        headerFieldsListVO.setSubjectType(subjectType);
        headerFieldsListVO.setSubjectId(subjectId);
        headerFieldsListVO.setSourceId(sourceId);
        headerFieldsListVO.setModuleCode(HeaderType.CUSTOMER.getValue());
        headerFieldsListVO.setIsEdit(true);
        headerFieldsListVO.setModuleType("客户明细");
        // 未创建自定义
        if (hadEmpName) {
            headerList.add(0, "姓名");
        }
        if (hadIdCard) {
            headerList.add(1, "身份证号");
        }
        if (hadSocCity) {
            headerList.add(2, "参保城市");
        }
        if (hadShouldWages) {
            headerList.add("应发工资");
        }
        if (hadEmpSocialSecurity) {
            headerList.add("个人社保");
        }
        if (hadEmpProvidentFund) {
            headerList.add("个人公积金");
        }
        if (hadTax) {
            headerList.add("个人所得税");
        }
        if (hadPaidWages) {
            headerList.add("实发工资");
        }
        if (hadEnterpriseSocialSecurity) {
            headerList.add("企业社保");
        }
        if (hadEnterpriseProvidentFund) {
            headerList.add("企业公积金");
        }
        if (hadLaborCosts) {
            headerList.add("人力成本");
        }
        if (hadFine) {
            headerList.add("社保滞纳金");
        }
        if (hadSalaryServiceCharge) {
            headerList.add("薪资服务费");
        }
        if (hadSalarySocialCharge) {
            headerList.add("社保服务费");
        }
        if (hadServiceCharge) {
            headerList.add("服务费");
        }
        Map<String, Boolean> fieldNameIsOpenMap = new HashMap<>();
        if (!org.springframework.util.CollectionUtils.isEmpty(headerFieldsDOList)) {
            // 已创建自定义
            fieldNameIsOpenMap = headerFieldsDOList.parallelStream().collect(Collectors
                    .toMap(HeaderFieldsDO::getFieldName, HeaderFieldsDO::getIsOpen));
        }
        for (int i = 0; i < headerList.size(); i++) {
            String header = headerList.get(i);
            HeaderFieldsListVO empName = headerFieldsListVO.to(HeaderFieldsListVO.class);
            empName.setFieldName(header);
            empName.setFieldEnName(SalaryConstants.CUSTOMER_DETAILS_MAP.get(header));
            empName.setSort(i);
            if (fieldNameIsOpenMap.get(header) == null) {
                empName.setIsOpen(true);
            } else {
                empName.setIsOpen(fieldNameIsOpenMap.get(header));
            }
            headerFieldsListVOList.add(empName);
        }
        return headerFieldsListVOList;
    }

    /**
     * 查询自定义分组的客户明细汇总
     *
     * @param yearly              年
     * @param monthly             月
     * @param customerDetailsType 客户明细表类型
     * @param customGroupingId    自定义分组Id
     * @param tokenUser           登录用户
     * @return 自定义分组的客户明细汇总
     */
    private HrMonthlySalarySummaryNewVO selectSalarySummaryByCustomGroupingId(Integer yearly, Integer monthly
            , String customerDetailsType, Long customGroupingId, TokenUser tokenUser) {
        CompanyCustomGroupingDO companyCustomGroupingDO = companyCustomGroupingService
                .selectDOById(customGroupingId, tokenUser.getSiteId());
        ParamException.notNull(companyCustomGroupingDO, "自定义分组信息有误，请刷新后重试");

        HrMonthlySalarySummaryNewVO hrMonthlySalarySummaryNewVO = new HrMonthlySalarySummaryNewVO();
        hrMonthlySalarySummaryNewVO.setName(companyCustomGroupingDO.getName());
        hrMonthlySalarySummaryNewVO.setPersonalPeriod(yearly + "年" + monthly + "月");
        // 类型是自定义分组
        hrMonthlySalarySummaryNewVO.setSubjectType("customGrouping");
        hrMonthlySalarySummaryNewVO.setSubjectId(customGroupingId + "");
        hrMonthlySalarySummaryNewVO.setCompanyIds(new ArrayList<>());
        hrMonthlySalarySummaryNewVO.setEmpNumber(0);
        hrMonthlySalarySummaryNewVO.setCustomerDetailsIds(new ArrayList<>());
        hrMonthlySalarySummaryNewVO.setDetailsAmountVOList(new ArrayList<>());
        hrMonthlySalarySummaryNewVO.setYearly(yearly);
        hrMonthlySalarySummaryNewVO.setMonthly(monthly);
        hrMonthlySalarySummaryNewVO.setGmtCreate(companyCustomGroupingDO.getGmtCreate());
        hrMonthlySalarySummaryNewVO.setCreateUserId(companyCustomGroupingDO.getCreateUserId());
        UserVO userVO = userProviderService.selectById(companyCustomGroupingDO.getCreateUserId());
        if (userVO != null) {
            hrMonthlySalarySummaryNewVO.setCreateUserName(userVO.getName());
        }
        hrMonthlySalarySummaryNewVO.setCustomerDetailsType(customerDetailsType);

        String companyIdList = companyCustomGroupingDO.getCompanyIds();
        if (org.apache.commons.lang3.StringUtils.isEmpty(companyIdList)) {
            return hrMonthlySalarySummaryNewVO;
        }
        List<String> companyIds = new ArrayList<>(Arrays.asList(companyIdList.split(",")));
        // 有权限的用工单位
        List<String> tileCompanyIdByUser = companyService.tileCompanyIdByUser(tokenUser);
        // 过滤掉没有权限的用工单位
        companyIds = companyIds.parallelStream().filter(tileCompanyIdByUser::contains).collect(Collectors.toList());
        if (org.springframework.util.CollectionUtils.isEmpty(companyIds)) {
            return hrMonthlySalarySummaryNewVO;
        }
        hrMonthlySalarySummaryNewVO.setCompanyIds(companyIds);
        List<CustomerDetailsNewVO> customerDetailsNewVOList = salaryProviderService
                .selectVoAndAmountByTypeAndCompanyIds(yearly, monthly, customerDetailsType, companyIds, tokenUser);
        if (org.springframework.util.CollectionUtils.isEmpty(customerDetailsNewVOList)) {
            return hrMonthlySalarySummaryNewVO;
        }
        // 明细金额
        List<CustomerDetailsNewServiceChargeDO> customerDetailsNewServiceChargeDOList = salaryProviderService
                .selectCustomerDetailsNewServiceChargeByCustomerDetailsNewIds(
                        customerDetailsNewVOList.parallelStream().map(CustomerDetailsNewVO::getId).collect(Collectors.toList()), tokenUser);
        // 客户明细表-服务费项
        Map<Long, List<CustomerDetailsNewServiceChargeDO>> customerDetailsNewIdListMap = customerDetailsNewServiceChargeDOList
                .parallelStream().collect(Collectors.groupingBy(CustomerDetailsNewServiceChargeDO::getCustomerDetailsNewId));
        int empNumber = 0;
        Map<String, BigDecimal> nameAmount = new HashMap<>(16);
        List<Long> customerDetailsIds = new ArrayList<>();
        for (CustomerDetailsNewVO customerDetailsNewVO : customerDetailsNewVOList) {
            // 客户明细Id
            customerDetailsIds.add(customerDetailsNewVO.getId());
            // 计薪人数
            if (customerDetailsNewVO.getEmpNumber() != null) {
                empNumber += customerDetailsNewVO.getEmpNumber();
            }
            // 明细金额
            List<DetailsAmountVO> detailsAmountVOList = customerDetailsNewVO.getDetailsAmountVOList();
            if (CollectionUtils.isEmpty(detailsAmountVOList)) {
                detailsAmountVOList = new ArrayList<>();
            }
            List<CustomerDetailsNewServiceChargeDO> customerDetailsNewServiceChargeList
                    = customerDetailsNewIdListMap.get(customerDetailsNewVO.getId());
            if (!CollectionUtils.isEmpty(customerDetailsNewServiceChargeList)) {
                for (CustomerDetailsNewServiceChargeDO customerDetailsNewServiceChargeDO : customerDetailsNewServiceChargeList) {
                    DetailsAmountVO detailsAmountVO = new DetailsAmountVO();
                    detailsAmountVO.setSourceType("customer");
                    detailsAmountVO.setSourceId(customerDetailsNewVO.getId());
                    detailsAmountVO.setName(customerDetailsNewServiceChargeDO.getName());
                    detailsAmountVO.setAmount(customerDetailsNewServiceChargeDO.getAmount());
                    detailsAmountVOList.add(detailsAmountVO);
                }
            }
            if (!org.springframework.util.CollectionUtils.isEmpty(detailsAmountVOList)) {
                for (DetailsAmountVO detailsAmountVO : detailsAmountVOList) {
                    BigDecimal amount;
                    try {
                        amount = new BigDecimal(detailsAmountVO.getAmount());
                    } catch (Exception e) {
                        continue;
                    }
                    nameAmount.merge(detailsAmountVO.getName(), amount, BigDecimal::add);
                }
            }
        }
        // 合计表头
        List<String> totalTableHeaderList = new ArrayList<>(TOTAL_TABLE_HEADER_LIST1);
        if (!CollectionUtils.isEmpty(customerDetailsNewServiceChargeDOList)) {
            totalTableHeaderList.addAll(customerDetailsNewServiceChargeDOList.parallelStream()
                    .map(CustomerDetailsNewServiceChargeDO::getName).distinct().collect(Collectors.toList()));
        }
        totalTableHeaderList.addAll(TOTAL_TABLE_HEADER_LIST2);
        List<DetailsAmountVO> detailsAmountVOList = new ArrayList<>();
        for (int i = 0; i < totalTableHeaderList.size(); i++) {
            String name = totalTableHeaderList.get(i);
            DetailsAmountVO detailsAmountVO = new DetailsAmountVO();
            detailsAmountVO.setSourceType("customGrouping");
            detailsAmountVO.setSourceId(customGroupingId);
            detailsAmountVO.setName(name);
            if (nameAmount.get(name) == null) {
                continue;
            }
            detailsAmountVO.setAmount(nameAmount.get(name).stripTrailingZeros().toPlainString());
            detailsAmountVO.setSort(i + 1);
            detailsAmountVOList.add(detailsAmountVO);
        }
        // 排序
        detailsAmountVOList = detailsAmountVOList.parallelStream()
                .sorted(Comparator.comparing(DetailsAmountVO::getSort)).collect(Collectors.toList());
        hrMonthlySalarySummaryNewVO.setEmpNumber(empNumber);
        hrMonthlySalarySummaryNewVO.setCustomerDetailsIds(customerDetailsIds);
        hrMonthlySalarySummaryNewVO.setDetailsAmountVOList(detailsAmountVOList);
        return hrMonthlySalarySummaryNewVO;
    }

    /**
     * 单纯保存客户明细表
     *
     * @param hrCustomerDetailsNewAddDTO 新增客户明细表DTO
     * @param yearly                     年
     * @param monthly                    月
     * @param salaryYearly               薪资年份
     * @param salaryMonthly              薪资月份
     * @param socialYearly               社保年份
     * @param socialMonthly              社保月份
     * @param companyIds                 用工单位Ids
     * @param companyInfoList            用工单位明细
     * @param tokenUser                  当前用户
     * @return 客户明细
     */
    private List<CustomerDetailsNewDO> saveCustomerDetails(HrCustomerDetailsNewAddDTO hrCustomerDetailsNewAddDTO
            , Integer yearly, Integer monthly, Integer salaryYearly, Integer salaryMonthly, Integer socialYearly
            , Integer socialMonthly, List<String> companyIds
            , List<HrCustomerDetailsNewAddDTO.CompanyInfo> companyInfoList, TokenUser tokenUser) {

        // 已创建的客户明细
        List<CustomerDetailsNewDO> oldDOList = salaryProviderService
                .selectNewCustomerDetailsByMonthlyCompanyIdAndType(yearly
                        , monthly, hrCustomerDetailsNewAddDTO.getType(), companyIds, tokenUser);
        // 删除已创建的 重新创建
        if (!CollectionUtils.isEmpty(oldDOList)) {
            salaryProviderService.deleteNewCustomerDetailsByIds(tokenUser
                    , oldDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }

        // 个人所属期
        hrCustomerDetailsNewAddDTO.setPersonalPeriod(yearly + "年" + monthly + "月");
        // 新建客户明细表
        List<CustomerDetailsNewDO> customerDetailsNewDOList = new ArrayList<>();
        // 月工资汇总
        List<MonthlySalarySummaryNewVO> monthlySalarySummaryNewVOList = salaryProviderService
                .selectMonthlySalarySummaryByCompanyIdsNew(companyIds, salaryYearly, salaryMonthly, tokenUser);
        // 用工单位Id-月工资汇总Id
        Map<String, Long> companyIdSummaryIdMap = monthlySalarySummaryNewVOList.parallelStream().collect(Collectors
                .toMap(MonthlySalarySummaryNewVO::getCompanyId, MonthlySalarySummaryNewVO::getMonthlySalaryId, (v1, v2) -> v1));
        for (HrCustomerDetailsNewAddDTO.CompanyInfo companyInfo : companyInfoList) {
            CustomerDetailsNewAddDTO customerDetailsNewAddDTO = hrCustomerDetailsNewAddDTO.to(CustomerDetailsNewAddDTO.class);
            customerDetailsNewAddDTO.setCompanyId(companyInfo.getCompanyId());
            customerDetailsNewAddDTO.setHeader(createHeader(companyInfo.getCompanyId(), companyIdSummaryIdMap.get(companyInfo.getCompanyId())
                    , salaryYearly, salaryMonthly, socialYearly, socialMonthly, tokenUser));
            String companyName = companyInfo.getCompanyName();
            companyName = companyName.replaceAll("&nbsp;", " ");
            customerDetailsNewAddDTO.setName(HtmlUtils.htmlUnescape(companyName));
            // 计薪人数暂时赋值0
            customerDetailsNewAddDTO.setEmpNumber(0);
            // 保存客户明细表
            CustomerDetailsNewDO customerDetailsNewDO = salaryProviderService.saveNewCustomerDetails(customerDetailsNewAddDTO, tokenUser);
            customerDetailsNewDOList.add(customerDetailsNewDO);
        }
        return customerDetailsNewDOList;
    }

    /**
     * 保存员工明细
     *
     * @param yearly                   年份
     * @param monthly                  月份
     * @param salaryYearly             薪资年份
     * @param salaryMonthly            薪资月份
     * @param socialYearly             社保年份
     * @param socialMonthly            社保月份
     * @param customerDetailsNewDOList 新建客户明细表
     * @param empIdAmountMap           empId-项目名称-金额
     * @param tokenUser                当前用户
     * @return 员工明细
     */
    private List<EmployeeDetailsNewDO> saveEmployeeDetails(Integer yearly, Integer monthly, List<String> companyIds
            , Integer salaryYearly, Integer salaryMonthly, Integer socialYearly, Integer socialMonthly
            , List<CustomerDetailsNewDO> customerDetailsNewDOList
            , Map<String, Map<String, BigDecimal>> empIdAmountMap, TokenUser tokenUser) {
        // 客户明细为空 跳过
        if (CollectionUtils.isEmpty(customerDetailsNewDOList)) {
            return new ArrayList<>();
        }
        // 计薪人员
        Set<String> empIds = new HashSet<>();
        // 员工月工资Id-员工金额List
        Map<Long, List<EmployeeAmountDO>> employeeSalaryNewIdListMap = new HashMap<>(16);
        // 使用薪资
        Map<String, EmployeeSalaryNewDO> empIdSalaryNewDoMap = new HashMap<>(16);
        if (salaryYearly != null && salaryMonthly != null) {
            // 员工月工资(新)
            List<EmployeeSalaryNewDO> employeeSalaryNewDOList = salaryProviderService.selectNewByCompanyIdsAndMonthly(
                    companyIds, salaryYearly, salaryMonthly, tokenUser);
            empIdSalaryNewDoMap = employeeSalaryNewDOList
                    .parallelStream().collect(Collectors.toMap(EmployeeSalaryNewDO::getEmpId, v -> v, (v1, v2) -> v1));
            empIds.addAll(empIdSalaryNewDoMap.keySet());
            // 员工金额
            List<EmployeeAmountDO> employeeAmountDOList = salaryProviderService
                    .selectAmountByCompanyIdsAndMonthly(companyIds, salaryYearly, salaryMonthly, tokenUser);
            employeeSalaryNewIdListMap = employeeAmountDOList.parallelStream()
                    .collect(Collectors.groupingBy(EmployeeAmountDO::getSaEmployeeSalaryNewId));
            // 薪资服务费(一次性)
            getServiceCharge(yearly, monthly, salaryYearly, salaryMonthly
                    , companyIds, employeeSalaryNewDOList, employeeSalaryNewIdListMap);
        }
        // 使用社保
        Map<String, EmpSocInfoVO> empIdSocInfoVoMap = new HashMap<>(16);
        if (socialYearly != null && socialMonthly != null) {
            List<EmpSocInfoVO> empSocInfoVOList = empSocInfoService.selectEmpSocInfoWithCustomerDetails(socialYearly
                    , socialMonthly, companyIds, tokenUser);
            empIdSocInfoVoMap = empSocInfoVOList.parallelStream()
                    .collect(Collectors.toMap(EmpSocInfoVO::getEmpId, v -> v, (v1, v2) -> v1));
            empIds.addAll(empIdSocInfoVoMap.keySet());
        }
        // 用工单位Id-客户明细表Id
        Map<String, Long> companyIdCustomerDetailsNewIdMap = customerDetailsNewDOList.parallelStream()
                .collect(Collectors.toMap(CustomerDetailsNewDO::getCompanyId, BaseDO::getId, (v1, v2) -> v1));
        List<EmployeeDetailsNewAddDTO> employeeDetailsNewAddDTOList = new ArrayList<>();

        // 计薪人员
        for (String empId : empIds) {
            // 项目名称-金额
            Map<String, BigDecimal> nameAmount = new HashMap<>(16);
            // 薪资模块
            String companyId = null;
            String empName = null;
            // 服务费
            BigDecimal serviceCharge = BigDecimal.ZERO;
            EmployeeSalaryNewDO employeeSalaryNewDO = empIdSalaryNewDoMap.get(empId);
            if (employeeSalaryNewDO != null) {
                companyId = employeeSalaryNewDO.getCompanyId();
                empName = employeeSalaryNewDO.getEmpName();
                // 员工金额List
                List<EmployeeAmountDO> employeeAmountDOList = employeeSalaryNewIdListMap.get(employeeSalaryNewDO.getId());
                if (!CollectionUtils.isEmpty(employeeAmountDOList)) {
                    for (EmployeeAmountDO employeeAmountDO : employeeAmountDOList) {
                        // 过滤企业社保公积金
                        if ("企业社保".equals(employeeAmountDO.getItemName())
                                || "企业公积金".equals(employeeAmountDO.getItemName())) {
                            continue;
                        }
                        if ("薪资服务费".equals(employeeAmountDO.getItemName())) {
                            serviceCharge = serviceCharge.add(BigDecimalKit.nullToZero(employeeAmountDO.getAmount()));
                        }
                        nameAmount.put(employeeAmountDO.getItemName(), employeeAmountDO.getAmount());
                    }
                }
            }
            // 社保模块
            EmpSocInfoVO empSocInfoVO = empIdSocInfoVoMap.get(empId);
            if (empSocInfoVO != null) {
                companyId = empSocInfoVO.getCompanyId();
                empName = empSocInfoVO.getEmpName();
                // 如果该员工没有创建月工资，且创建了社保
                if (employeeSalaryNewDO == null) {
                    // 人力成本 = 企业社保+企业公积金
                    nameAmount.put("人力成本", BigDecimalKit.nullToZero(empSocInfoVO.getCompanySocAmount())
                            .add(BigDecimalKit.nullToZero(empSocInfoVO.getCompanyPfAmount())));
                    nameAmount.put("个人社保", empSocInfoVO.getPersonalSocAmount());
                    nameAmount.put("个人公积金", empSocInfoVO.getPersonalPfAmount());
                }
                nameAmount.put("企业社保", empSocInfoVO.getCompanySocAmount());
                nameAmount.put("企业公积金", empSocInfoVO.getCompanyPfAmount());
                nameAmount.put("社保服务费", empSocInfoVO.getServiceCharge());
                if (empSocInfoVO.getServiceCharge() != null) {
                    serviceCharge = serviceCharge.add(empSocInfoVO.getServiceCharge());
                }
                // 社保滞纳金
                BigDecimal fine = empSocInfoVO.getAccumulationFundFine().add(empSocInfoVO.getSocialSecurityFine());
                if (BigDecimal.ZERO.compareTo(fine) != 0) {
                    nameAmount.put("社保滞纳金", fine);
                }
            }
            nameAmount.put("服务费", serviceCharge);
            // 用工单位为空 跳过
            if (companyId == null) {
                log.error("用工单位为空,empId:{},employeeSalaryNewDO:{},empSocInfoVO:{}", empId, employeeSalaryNewDO, empSocInfoVO);
                continue;
            }
            if (empName == null) {
                log.error("员工姓名为空,empId:{},employeeSalaryNewDO:{},empSocInfoVO:{}", empId, employeeSalaryNewDO, empSocInfoVO);
                continue;
            }
            Long customerDetailsNewId = companyIdCustomerDetailsNewIdMap.get(companyId);
            if (customerDetailsNewId == null) {
                log.error("未创建客户明细表,empId:{},employeeSalaryNewDO:{},empSocInfoVO:{}", empId, employeeSalaryNewDO, empSocInfoVO);
                continue;
            }
            empIdAmountMap.put(empId, nameAmount);
            EmployeeDetailsNewAddDTO employeeDetailsNewAddDTO = new EmployeeDetailsNewAddDTO();
            employeeDetailsNewAddDTO.setCompanyId(companyId);
            employeeDetailsNewAddDTO.setEmpId(empId);
            employeeDetailsNewAddDTO.setEmpName(empName);
            employeeDetailsNewAddDTO.setYearly(yearly);
            employeeDetailsNewAddDTO.setMonthly(monthly);
            employeeDetailsNewAddDTO.setCustomerDetailsNewId(customerDetailsNewId);
            employeeDetailsNewAddDTOList.add(employeeDetailsNewAddDTO);
        }
        // 保存员工明细
        if (CollectionUtils.isEmpty(employeeDetailsNewAddDTOList)) {
            return new ArrayList<>();
        }
        return salaryProviderService.batchSaveNewEmployeeDetails(employeeDetailsNewAddDTOList, tokenUser);
    }

    /**
     * 获取一次性服务费
     *
     * @param yearly                     客户明细年份
     * @param monthly                    客户明细月份
     * @param salaryYearly               薪资年份
     * @param salaryMonthly              薪资月份
     * @param companyIds                 用工单位Ids
     * @param employeeSalaryNewDOList    员工月工资
     * @param employeeSalaryNewIdListMap 员工月工资Id-员工金额
     */
    private void getServiceCharge(Integer yearly, Integer monthly, Integer salaryYearly, Integer salaryMonthly
            , List<String> companyIds, List<EmployeeSalaryNewDO> employeeSalaryNewDOList
            , Map<Long, List<EmployeeAmountDO>> employeeSalaryNewIdListMap) {
        // 用工单位下的薪资组
        List<SalaryGroupDO> salaryGroupDOList = salaryProviderService.selectSalaryGroupByCompanyIds(companyIds);
        // 已创建的薪资组月工资
        List<SalaryGroupSalaryDO> salaryGroupSalaryDOList
                = salaryProviderService.selectCreatedSalaryGroupByCompanyIds(salaryYearly, salaryMonthly, companyIds);
        // 已创建月工资表的薪资组
        List<Long> hadSalaryGroupIds = salaryGroupSalaryDOList
                .parallelStream().map(SalaryGroupSalaryDO::getSalaryGroupId).collect(Collectors.toList());
        // 薪资组Id-VO
        Map<Long, SalaryGroupDO> salaryGroupIdDoMap = new HashMap<>(16);

        // 用工单位Id-默认薪资组
        Map<String, SalaryGroupDO> companyIdAllEmpGroupMap = new HashMap<>(16);
        for (SalaryGroupDO salaryGroupDO : salaryGroupDOList) {
            if (salaryGroupDO.getServiceFeeType() == 3 && hadSalaryGroupIds.contains(salaryGroupDO.getId())) {
                // 一次性费用 且是当月收费 需要统计到公司的客户明细中
                SimpleDateFormat format = new SimpleDateFormat("yyyyMM");
                BigDecimal chargeAmount = salaryGroupDO.getChargeAmount();
                // 客户明细
                String billingMonth;
                if (monthly < 10) {
                    billingMonth = "" + yearly + "0" + monthly;
                } else {
                    billingMonth = "" + yearly + monthly;
                }
                if (billingMonth.equals(format.format(salaryGroupDO.getBillingMonth()))
                        && chargeAmount != null) {
                    oneServiceChargeAmount = oneServiceChargeAmount.add(chargeAmount);
                }
            }
            salaryGroupIdDoMap.put(salaryGroupDO.getId(), salaryGroupDO);
            // 取出全员薪资组
            if (salaryGroupDO.getIsAllEmp() != null && salaryGroupDO.getIsAllEmp()) {
                companyIdAllEmpGroupMap.put(salaryGroupDO.getCompanyId(), salaryGroupDO);
            }
        }
        if (CollectionUtils.isEmpty(employeeSalaryNewDOList)) {
            return;
        }
        for (EmployeeSalaryNewDO employeeSalaryNewDO : employeeSalaryNewDOList) {
            String companyId = employeeSalaryNewDO.getCompanyId();
            SalaryGroupDO salaryGroupDO = salaryGroupIdDoMap.get(employeeSalaryNewDO.getSalaryGroupId());
            // 若员工所在薪资组为空 认为在全员薪资组中
            if (salaryGroupDO == null) {
                salaryGroupDO = companyIdAllEmpGroupMap.get(companyId);
            }
            if (salaryGroupDO == null) {
                // 薪资组被删除 同时还没有默认薪资组(理论上不会出现)
                log.error("薪资组被删除 同时还没有默认薪资组,companyId:{}", companyId);
            } else {
                // 员工金额
                List<EmployeeAmountDO> employeeAmountDOList = employeeSalaryNewIdListMap.get(employeeSalaryNewDO.getId());
                if (employeeAmountDOList == null) {
                    employeeAmountDOList = new ArrayList<>();
                }
                Map<String, BigDecimal> nameBigDecimalMap = employeeAmountDOList.parallelStream()
                        .collect(Collectors.toMap(EmployeeAmountDO::getItemName, EmployeeAmountDO::getAmount));
                EmployeeAmountDO employeeAmountDO = new EmployeeAmountDO();
                employeeAmountDO.setItemName("薪资服务费");
                employeeAmountDO.setAmount(BigDecimal.ZERO);
                switch (salaryGroupDO.getServiceFeeType()) {
                    case 1:
                        // 按人头计算
                        employeeAmountDO.setAmount(salaryGroupDO.getChargeAmount());
                        break;
                    case 2:
                        // 百分比
                        BigDecimal proportion = salaryGroupDO.getProportion()
                                .divide(new BigDecimal("100"), RoundingMode.HALF_EVEN);
                        ParamException.notNull(proportion, "百分比不能为空");
                        BigDecimal bigDecimal = nameBigDecimalMap.get(salaryGroupDO.getServiceFeeItem());
                        // 按工资统计项计算
                        employeeAmountDO.setAmount(BigDecimalKit.nullToZero(bigDecimal).multiply(proportion)
                                .setScale(4, RoundingMode.HALF_UP));
                        break;
                }
                employeeAmountDOList.add(employeeAmountDO);
                employeeSalaryNewIdListMap.put(employeeSalaryNewDO.getId(), employeeAmountDOList);
            }
        }
    }

    /**
     * 保存明细金额(员工明细+客户明细)
     *
     * @param customerDetailsNewDOList 客户明细
     * @param employeeDetailsNewDOList 员工明细
     * @param empIdAmountMap           员工Id-金额
     * @param serviceChargeIds         工资项Ids
     * @param tokenUser                当前用户
     */
    private Map<Long, List<DetailsAmountDO>> saveDetailsAmount(List<CustomerDetailsNewDO> customerDetailsNewDOList
            , List<EmployeeDetailsNewDO> employeeDetailsNewDOList
            , Map<String, Map<String, BigDecimal>> empIdAmountMap, List<Long> serviceChargeIds, TokenUser tokenUser) {
        // 客户明细Id-客户明细金额
        Map<Long, List<DetailsAmountDO>> customerDetailsNewIdListMap = new HashMap<>(16);
        // 客户明细为空 直接返回
        if (CollectionUtils.isEmpty(customerDetailsNewDOList)) {
            return customerDetailsNewIdListMap;
        }
        List<DetailsAmountAddDTO> detailsAmountAddDTOList = new ArrayList<>();
        // 服务费项
        List<String> serviceChargeName = new ArrayList<>();
        List<ServiceChargeDO> serviceChargeDOList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(serviceChargeIds)) {
            serviceChargeDOList = salaryProviderService.selectServiceChargeByIds(serviceChargeIds, tokenUser);
            List<CustomerDetailsNewServiceChargeAddDTO> addDTOList = new ArrayList<>();
            for (ServiceChargeDO serviceChargeDO : serviceChargeDOList) {
                for (CustomerDetailsNewDO customerDetailsNewDO : customerDetailsNewDOList) {
                    CustomerDetailsNewServiceChargeAddDTO addDTO = new CustomerDetailsNewServiceChargeAddDTO();
                    addDTO.setCustomerDetailsNewId(customerDetailsNewDO.getId());
                    addDTO.setName(serviceChargeDO.getName());
                    addDTO.setAmount(serviceChargeDO.getAmount());
                    addDTOList.add(addDTO);
                }
                serviceChargeName.add(serviceChargeDO.getName());
            }
            salaryProviderService.batchSaveCustomerDetailsNewServiceCharge(addDTOList, tokenUser);
            // 员工明细为空 仅保存客户明细金额中的服务费
            if (CollectionUtils.isEmpty(employeeDetailsNewDOList)) {
                for (CustomerDetailsNewDO customerDetailsNewDO : customerDetailsNewDOList) {
                    // 服务费项的总金额
                    String amount = serviceChargeDOList.parallelStream().map(serviceChargeDO
                            -> new BigDecimal(serviceChargeDO.getAmount())).reduce(BigDecimal.ZERO, BigDecimal::add).stripTrailingZeros().toPlainString();
                    DetailsAmountAddDTO detailsAmountAddDTO = new DetailsAmountAddDTO();
                    detailsAmountAddDTO.setSourceType("customer");
                    detailsAmountAddDTO.setSourceId(customerDetailsNewDO.getId());
                    detailsAmountAddDTO.setName("服务费");
                    detailsAmountAddDTO.setAmount(amount);
                    detailsAmountAddDTOList.add(detailsAmountAddDTO);
                    // 客户费用
                    DetailsAmountAddDTO customerFeesAddDTO = new DetailsAmountAddDTO();
                    customerFeesAddDTO.setSourceType("customer");
                    customerFeesAddDTO.setSourceId(customerDetailsNewDO.getId());
                    customerFeesAddDTO.setName("客户费用");
                    customerFeesAddDTO.setAmount(amount);
                    detailsAmountAddDTOList.add(customerFeesAddDTO);
                }
                List<DetailsAmountDO> detailsAmountDOList = salaryProviderService.batchSaveNewDetailsAmount(detailsAmountAddDTOList, tokenUser);
                customerDetailsNewIdListMap = detailsAmountDOList.parallelStream().filter(detailsAmountDO -> "customer"
                        .equals(detailsAmountDO.getSourceType())).collect(Collectors.groupingBy(DetailsAmountDO::getSourceId));
                return customerDetailsNewIdListMap;
            }
        }

        // 合计表头
        List<String> totalTableHeaderList = new ArrayList<>(TOTAL_TABLE_HEADER_LIST1);
        totalTableHeaderList.addAll(serviceChargeName);
        totalTableHeaderList.addAll(TOTAL_TABLE_HEADER_LIST2);
        // 员工明细按照用工单位Id分组
        Map<String, List<EmployeeDetailsNewDO>> companyIdListMap = employeeDetailsNewDOList.parallelStream()
                .collect(Collectors.groupingBy(EmployeeDetailsNewDO::getCompanyId));
        for (CustomerDetailsNewDO customerDetailsNewDO : customerDetailsNewDOList) {
            List<EmployeeDetailsNewDO> employeeDetailsNewList = companyIdListMap.get(customerDetailsNewDO.getCompanyId());
            // 员工明细为空，跳过
            if (CollectionUtils.isEmpty(employeeDetailsNewList)) {
                continue;
            }
            // 客户明细金额
            Map<String, BigDecimal> companyNameAmount = new HashMap<>(16);
            // 员工金额
            for (EmployeeDetailsNewDO employeeDetailsNewDO : employeeDetailsNewList) {
                Map<String, BigDecimal> nameAmount = empIdAmountMap.get(employeeDetailsNewDO.getEmpId());
                if (nameAmount == null) {
                    continue;
                }
                // 员工明细金额
                for (String name : nameAmount.keySet()) {
                    BigDecimal amount = nameAmount.get(name);
                    if (amount == null) {
                        continue;
                    }
                    DetailsAmountAddDTO detailsAmountAddDTO = new DetailsAmountAddDTO();
                    detailsAmountAddDTO.setSourceType("employee");
                    detailsAmountAddDTO.setSourceId(employeeDetailsNewDO.getId());
                    detailsAmountAddDTO.setName(name);
                    detailsAmountAddDTO.setAmount(amount.stripTrailingZeros().toPlainString());
                    detailsAmountAddDTOList.add(detailsAmountAddDTO);
                    companyNameAmount.merge(name, amount, BigDecimal::add);
                }
            }
            // 人力成本
            BigDecimal laborCost = BigDecimal.ZERO;
            // 服务费
            BigDecimal serviceCharge = BigDecimal.ZERO;
            // 社保滞纳金
            BigDecimal socialSecurityFine = BigDecimal.ZERO;
            // 客户明细金额
            for (String name : companyNameAmount.keySet()) {
                // 不属于表头的跳过
                if (!totalTableHeaderList.contains(name)) {
                    continue;
                }
                BigDecimal amount = companyNameAmount.get(name);
                if ("人力成本".equals(name)) {
                    laborCost = amount;
                }
                if ("薪资服务费".equals(name)) {
                    // 薪资服务费放入一次性服务费
                    amount = amount.add(oneServiceChargeAmount);
                }
                if ("服务费".equals(name)) {
                    // 服务费放入一次性服务费
                    amount = amount.add(oneServiceChargeAmount);
                    // 服务费放入服务费项金额
                    for (ServiceChargeDO serviceChargeDO : serviceChargeDOList) {
                        amount = amount.add(new BigDecimal(serviceChargeDO.getAmount()));
                    }
                    serviceCharge = amount;
                }
                if ("社保滞纳金".equals(name)) {
                    socialSecurityFine = amount;
                }
                DetailsAmountAddDTO detailsAmountAddDTO = new DetailsAmountAddDTO();
                detailsAmountAddDTO.setSourceType("customer");
                detailsAmountAddDTO.setSourceId(customerDetailsNewDO.getId());
                detailsAmountAddDTO.setName(name);
                detailsAmountAddDTO.setAmount(amount.stripTrailingZeros().toPlainString());
                detailsAmountAddDTOList.add(detailsAmountAddDTO);
            }
            // 客户费用 = 人力成本 + 服务费 + 社保滞纳金
            DetailsAmountAddDTO detailsAmountAddDTO = new DetailsAmountAddDTO();
            detailsAmountAddDTO.setSourceType("customer");
            detailsAmountAddDTO.setSourceId(customerDetailsNewDO.getId());
            detailsAmountAddDTO.setName("客户费用");
            detailsAmountAddDTO.setAmount(laborCost.add(serviceCharge).add(socialSecurityFine).stripTrailingZeros().toPlainString());
            detailsAmountAddDTOList.add(detailsAmountAddDTO);
            // 更新计薪人数
            customerDetailsNewDO.setEmpNumber(employeeDetailsNewList.size());
            // 滞纳金为空 表头除去滞纳金
            if (companyNameAmount.get("社保滞纳金") == null || companyNameAmount.get("社保滞纳金").compareTo(BigDecimal.ZERO) == 0) {
                customerDetailsNewDO.setHeader(customerDetailsNewDO.getHeader().replaceAll(",社保滞纳金", ""));
            }
            salaryProviderService.updateNewCustomerDetails(customerDetailsNewDO.to(CustomerDetailsNewUpdateDTO.class), tokenUser);
        }
        if (!CollectionUtils.isEmpty(detailsAmountAddDTOList)) {
            List<DetailsAmountDO> detailsAmountDOList = salaryProviderService.batchSaveNewDetailsAmount(detailsAmountAddDTOList, tokenUser);
            customerDetailsNewIdListMap = detailsAmountDOList.parallelStream().filter(detailsAmountDO -> "customer"
                    .equals(detailsAmountDO.getSourceType())).collect(Collectors.groupingBy(DetailsAmountDO::getSourceId));
        }
        return customerDetailsNewIdListMap;
    }


    /**
     * 创建客户结算
     *
     * @param type                        客户明细表类型
     * @param customerDetailsNewDOList    客户明细表
     * @param customerDetailsNewIdListMap 客户明细表Id-客户金额
     * @param tokenUser                   当前用户
     */
    private void saveCustomerSettlement(String type, List<CustomerDetailsNewDO> customerDetailsNewDOList
            , Map<Long, List<DetailsAmountDO>> customerDetailsNewIdListMap, TokenUser tokenUser) {
        // 仅客户明细创建客户结算
        if (!CUSTOMER.getValue().equals(type)) {
            return;
        }
        List<CustomerSettlementAddDTO> customerSettlementAddDTOList = customerDetailsNewDOList.parallelStream().map(customerDetailsNewDO -> {
            List<DetailsAmountDO> detailsAmountDOList = customerDetailsNewIdListMap.get(customerDetailsNewDO.getId());
            if (CollectionUtils.isEmpty(detailsAmountDOList)) {
                return null;
            }
            // 客户金额名称-金额
            Map<String, String> nameAmountMap = detailsAmountDOList.parallelStream()
                    .collect(Collectors.toMap(DetailsAmountDO::getName, DetailsAmountDO::getAmount));
            // 应到账金额 = 客户费用
            if (StringUtils.isEmpty(nameAmountMap.get("客户费用"))) {
                return null;
            }
            BigDecimal totalAmount;
            try {
                totalAmount = new BigDecimal(nameAmountMap.get("客户费用"));
            } catch (Exception e) {
                return null;
            }
            if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
                return null;
            }

            // 人力成本
            BigDecimal laborCost;
            if (StringUtils.isEmpty(nameAmountMap.get("人力成本"))) {
                laborCost = BigDecimal.ZERO;
            } else {
                try {
                    laborCost = new BigDecimal(nameAmountMap.get("人力成本"));
                } catch (Exception e) {
                    return null;
                }
            }

            // 社保滞纳金
            BigDecimal fine;
            if (StringUtils.isEmpty(nameAmountMap.get("社保滞纳金"))) {
                fine = BigDecimal.ZERO;
            } else {
                try {
                    fine = new BigDecimal(nameAmountMap.get("社保滞纳金"));
                } catch (Exception e) {
                    return null;
                }
            }

            // 服务费
            BigDecimal serviceFeeAmount;
            if (StringUtils.isEmpty(nameAmountMap.get("服务费"))) {
                serviceFeeAmount = BigDecimal.ZERO;
            } else {
                try {
                    serviceFeeAmount = new BigDecimal(nameAmountMap.get("服务费"));
                } catch (Exception e) {
                    return null;
                }
            }

            CustomerSettlementAddDTO customerSettlementAddDTO = new CustomerSettlementAddDTO();
            customerSettlementAddDTO.setCompanyId(customerDetailsNewDO.getCompanyId());
            customerSettlementAddDTO.setCompanyName(customerDetailsNewDO.getName());
            // 应到账金额 = 客户费用
            customerSettlementAddDTO.setLaborCost(laborCost);
            customerSettlementAddDTO.setFine(fine);
            customerSettlementAddDTO.setMonthly(customerDetailsNewDO.getMonthly());
            customerSettlementAddDTO.setYearly(customerDetailsNewDO.getYearly());
            customerSettlementAddDTO.setServiceFeeAmount(serviceFeeAmount);
            customerSettlementAddDTO.setTotalAmount(totalAmount);
            customerSettlementAddDTO.setAmountPaid(BigDecimal.ZERO);
            customerSettlementAddDTO.setInvoicedAmount(BigDecimal.ZERO);
            return customerSettlementAddDTO;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(customerSettlementAddDTOList)) {
            salaryProviderService.batchSaveCustomerSettlement(customerSettlementAddDTOList, tokenUser);
        }
    }


    /**
     * 构建表头
     *
     * @param companyId     用工单位Id
     * @param summaryId     月工资汇总Id
     * @param salaryYearly  薪资来源年
     * @param salaryMonthly 薪资来源月
     * @param socialYearly  社保来源年
     * @param socialMonthly 社保来源月
     * @param tokenUser     当前用户
     * @return 表头
     */
    private String createHeader(String companyId, Long summaryId, Integer salaryYearly, Integer salaryMonthly
            , Integer socialYearly, Integer socialMonthly, TokenUser tokenUser) {
        // 没有薪资年月 返回社保表头
        if (salaryYearly == null || salaryMonthly == null) {
            if (socialYearly == null || socialMonthly == null) {
                return "";
            } else {
                return "姓名,身份证号,企业社保,企业公积金,人力成本,社保滞纳金,社保服务费,服务费";
            }
        }

        // 月工资汇总表头字段
        List<String> nameList;
        if (summaryId != null) {
            List<HeaderFieldsListVO> headerFieldsListVOList = salaryProviderService
                    .selectAllHeaderDetailNew(companyId, "monthly", summaryId, tokenUser);
            nameList = headerFieldsListVOList.stream().map(HeaderFieldsListVO::getFieldName).collect(Collectors.toList());
            // 如果创建了月工资表 表头加入薪资服务费
            nameList.add("薪资服务费");
        } else {
            log.error("该用工单位未创建月工资，用工单位Id：{}，薪资年月：{}", companyId, salaryYearly + "年" + salaryMonthly + "月");
            nameList = new ArrayList<>();
        }
        // 指定顺序
        nameList.removeIf("姓名"::equals);
        nameList.removeIf("身份证号"::equals);
        nameList.removeIf("企业社保"::equals);
        nameList.removeIf("企业公积金"::equals);
        nameList.removeIf("人力成本"::equals);
        boolean noTaxSalary = nameList.removeIf("不计税项"::equals);
        boolean shouldWages = nameList.removeIf("应发工资"::equals);
        boolean empSocialSecurity = nameList.removeIf("个人社保"::equals);
        boolean empProvidentFund = nameList.removeIf("个人公积金"::equals);
        boolean tax = nameList.removeIf("个人所得税"::equals);
        boolean paidWages = nameList.removeIf("实发工资"::equals);
        boolean salaryServiceCharge = nameList.removeIf("薪资服务费"::equals);
        String header = "姓名,身份证号";
        if (!CollectionUtils.isEmpty(nameList)) {
            header += "," + String.join(",", nameList);
        }
        // 含有社保年月
        boolean hadSocial = socialYearly != null && socialMonthly != null;
        if (noTaxSalary) {
            header += ",不计税项";
        }
        if (shouldWages) {
            header += ",应发工资";
        }
        if (empSocialSecurity) {
            header += ",个人社保";
        }
        if (empProvidentFund) {
            header += ",个人公积金";
        }
        if (tax) {
            header += ",个人所得税";
        }
        if (hadSocial) {
            header += ",企业社保,企业公积金";
        }
        if (paidWages) {
            header += ",实发工资";
        }
        header += ",人力成本";
        if (hadSocial) {
            header += ",社保滞纳金";
        }
        if (salaryServiceCharge) {
            header += ",薪资服务费";
        }
        if (hadSocial) {
            header += ",社保服务费";
        }
        header += ",服务费";
        return header;
    }

}




